home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2007 December / PCWKCD1207B.iso / Blogowanie poza sfera / Flock 0.9.1.3 stable / flock-0.9.1.3.en-US.win32.exe / flock / chrome / browser.jar / content / browser / bookmarks / bookmarksMenu.js < prev    next >
Text File  |  2007-08-16  |  39KB  |  1,117 lines

  1. //@line 38 "/cygdrive/K/tinderbuild/src/flock/mozilla/browser/components/bookmarks/content/bookmarksMenu.js"
  2.  
  3. const LIVEMARK_CAP = 20; // TODO: put that in a hidden option? (about:config)
  4.  
  5. var BookmarksMenu = {
  6.   _selection:null,
  7.   _target:null,
  8.   _orientation:null,
  9.   _folderRoot: "http://flock.com/rdf#BookmarksRoot",
  10.  
  11.   /////////////////////////////////////////////////////////////////////////////
  12.   // prepare the bookmarks menu for display
  13.   onShowMenu: function (aTarget)
  14.   {
  15.     if (aTarget.parentNode.getAttribute("livemark"))
  16.       this.showFeeditems(aTarget);
  17.     this.showOpenInTabsMenuItem(aTarget);
  18.     this.showEmptyItem(aTarget);
  19.   },
  20.  
  21.   /////////////////////////////////////////////////////////////////////////////
  22.   // remove arbitary elements created in this.onShowMenu()
  23.   onHideMenu: function (aTarget)
  24.   {
  25.     if (aTarget.parentNode.getAttribute("livemark")) {
  26.       this.hideAll(aTarget);
  27.     }
  28.     else {
  29.       this.hideOpenInTabsMenuItem(aTarget);
  30.       this.hideEmptyItem(aTarget);
  31.     }
  32.   },
  33.  
  34.   /////////////////////////////////////////////////////////////////////////////
  35.   // Populate livemarks with feeditems
  36.   showFeeditems: function (aTarget) {
  37.     var coopLivemark = faves_coop.get(aTarget.parentNode.id);
  38.     var reverseEnum = coopLivemark.children.enumerateBackwards();
  39.     var count = 0;
  40.     while (reverseEnum.hasMoreElements() && (count < LIVEMARK_CAP)) {
  41.       var feeditem = reverseEnum.getNext();
  42.       var element = document.createElementNS(gXUL_NS, "menuitem");
  43.       element.setAttribute("id", feeditem.id());
  44.       element.setAttribute("label", feeditem.name);
  45.       element.setAttribute("feeditem", "true");
  46.       element.setAttribute("class", "menuitem-iconic bookmark-item");
  47.       aTarget.appendChild(element);
  48.       element.setAttribute("statustext", feeditem.id);
  49.       count++;
  50.     }
  51.   },
  52.  
  53.   /////////////////////////////////////////////////////////////////////////////
  54.   // Remove all the children: used for livemark since we're populating in onShowMenu
  55.   hideAll: function (aTarget) {
  56.     while (aTarget.firstChild)
  57.       aTarget.removeChild(aTarget.firstChild);
  58.   },
  59.  
  60.   /////////////////////////////////////////////////////////////////////////////
  61.   // shows the 'Open All in Tabs' menu item if validOpenInTabsMenuItem is true -->
  62.   showOpenInTabsMenuItem: function (aTarget)
  63.   {
  64.     if (!this.validOpenInTabsMenuItem(aTarget) ||
  65.         aTarget.lastChild.getAttribute("class") == "openintabs-menuitem")
  66.       return;
  67.     var element = document.createElementNS(gXUL_NS, "menuseparator");
  68.     element.setAttribute("class", "openintabs-menuseparator");
  69.     aTarget.appendChild(element);
  70.     element = document.createElementNS(gXUL_NS, "menuitem");
  71.     element.setAttribute("class", "openintabs-menuitem");
  72.     element.setAttribute("label", BookmarksUtils.getLocaleString("cmd_bm_openfolder"));
  73.     element.setAttribute("accesskey", BookmarksUtils.getLocaleString("cmd_bm_openfolder_accesskey"));
  74.     aTarget.appendChild(element);
  75.   },
  76.  
  77.   realHideOpenInTabsMenuItem: function (aParent)
  78.   {
  79.     if (!aParent.hasChildNodes())
  80.       return;
  81.     var child = aParent.lastChild;
  82.     var removed = 0;
  83.     while (child) {
  84.       var cclass = child.getAttribute("class");
  85.       if (cclass == "openintabs-menuitem" || cclass == "openintabs-menuseparator") {
  86.         var prevchild = child.previousSibling;
  87.         aParent.removeChild(child);
  88.         child = prevchild;
  89.         removed++;
  90.         if (removed == 2)
  91.           break;
  92.       } else {
  93.         child = child.previousSibling;
  94.       }
  95.     }
  96.   },
  97.  
  98. //@line 143 "/cygdrive/K/tinderbuild/src/flock/mozilla/browser/components/bookmarks/content/bookmarksMenu.js"
  99.   /////////////////////////////////////////////////////////////////////////////
  100.   // hides the 'Open All in Tabs' on popuphidden so that we won't duplicate it -->
  101.   hideOpenInTabsMenuItem: function (aTarget)
  102.   {
  103.     BookmarksMenu.realHideOpenInTabsMenuItem(aTarget);
  104.   },
  105. //@line 150 "/cygdrive/K/tinderbuild/src/flock/mozilla/browser/components/bookmarks/content/bookmarksMenu.js"
  106.  
  107.   /////////////////////////////////////////////////////////////////////////////
  108.   // returns false if...
  109.   // - the parent is the bookmark menu or the chevron
  110.   // - the menupopup contains ony one bookmark
  111.   validOpenInTabsMenuItem: function (aTarget)
  112.   {
  113.     var rParent = RDF.GetResource(aTarget.parentNode.id)
  114.     var type = BookmarksUtils.resolveType(rParent);
  115.     if (type != "Folder" && type != "PersonalToolbarFolder" && type != "Livemark")
  116.       return false;
  117.     var count = 0;
  118.     if (!aTarget.hasChildNodes())
  119.       return false;
  120.     var curr = aTarget.firstChild;
  121.     do {
  122.       type = BookmarksUtils.resolveType(curr.id);
  123.       if ((type == "Bookmark" || type == "ImmutableBookmark") && ++count == 2)
  124.         return true;
  125.       curr = curr.nextSibling;
  126.     } while (curr);
  127.     return false;
  128.   },
  129.  
  130.   /////////////////////////////////////////////////////////////////////////////
  131.   // show an empty item if the menu is empty
  132.   showEmptyItem: function (aTarget)
  133.   {
  134.     if(aTarget.hasChildNodes())
  135.       return;
  136.  
  137.     var EmptyMsg = BookmarksUtils.getLocaleString("emptyFolder");
  138.     var emptyElement = document.createElementNS(gXUL_NS, "menuitem");
  139.     emptyElement.setAttribute("id", "empty-menuitem");
  140.     emptyElement.setAttribute("label", EmptyMsg);
  141.     emptyElement.setAttribute("disabled", "true");
  142.  
  143.     aTarget.appendChild(emptyElement);
  144.   },
  145.  
  146.   /////////////////////////////////////////////////////////////////////////////
  147.   // remove the empty element
  148.   hideEmptyItem: function (aTarget)
  149.   {
  150.     if (!aTarget.hasChildNodes())
  151.       return;
  152.  
  153.     // if the user drags to the menu while it's open (i.e. on the toolbar),
  154.     // the bookmark gets added either before or after the Empty menu item
  155.     // before the menu is hidden.  So we need to test both first and last.
  156.     if (aTarget.firstChild.id == "empty-menuitem")
  157.       aTarget.removeChild(aTarget.firstChild);
  158.     else if (aTarget.lastChild.id == "empty-menuitem")
  159.       aTarget.removeChild(aTarget.lastChild);
  160.   },
  161.  
  162.   //////////////////////////////////////////////////////////////////////////
  163.   // Fill a context menu popup with menuitems appropriate for the current
  164.   // selection.
  165.   createContextMenu: function (aEvent)
  166.   {
  167.     var target = document.popupNode;
  168.  
  169.     if (!this.isBTBookmark(target.id)) {
  170.       target.removeAttribute("open");
  171.       return false;
  172.     }
  173.  
  174.     var targettype = BookmarksUtils.resolveType(target.id);
  175.  
  176.     if (targettype == "ImmutableFolder") {
  177.       // no context; see bug#... (popups getting stuck because "open"
  178.       // attribute doesn't get removed)
  179.       target.removeAttribute("open");
  180.       return false;
  181.     }
  182.  
  183.     // -moz-user-focus: ignore; is set on toolbars
  184.     document.getElementById("PersonalToolbar").focus();
  185.  
  186.     this._selection   = this.getBTSelection(target);
  187.     this._orientation = this.getBTOrientation(aEvent, target);
  188.     if (targettype != "ImmutableBookmark")
  189.       this._target = this.getBTTarget(target, this._orientation);
  190.  
  191.     // walk up the tree until we find a database node
  192.     var p = target;
  193.     while (p && !p.database)
  194.       p = p.parentNode;
  195.     if (p)
  196.       this._db = p.database;
  197.  
  198.     BookmarksCommand.createContextMenu(aEvent, this._selection, this._db);
  199.     this.onCommandUpdate();
  200.     aEvent.target.addEventListener("mousemove", BookmarksMenuController.onMouseMove, false);
  201.     return true;
  202.   },
  203.  
  204.   /////////////////////////////////////////////////////////////////////////
  205.   // Clean up after closing the context menu popup
  206.   destroyContextMenu: function (aEvent)
  207.   {
  208.     // note that this method is called after doCommand.
  209.     // let's focus the content and dismiss the popup chain (needed when the user
  210.     // type escape or if he/she clicks outside the context menu)
  211.     if (content)
  212.       content.focus();
  213.     // XXXpch: see bug 210910, it should be done properly in the backend
  214.     BookmarksMenuDNDObserver.mCurrentDragOverTarget = null;
  215.     BookmarksMenuDNDObserver.onDragCloseTarget();
  216.  
  217.     // if the user types escape, we need to remove the feedback
  218.     BookmarksMenuDNDObserver.onDragRemoveFeedBack(document.popupNode);
  219.  
  220.     aEvent.target.removeEventListener("mousemove", BookmarksMenuController.onMouseMove, false);
  221.  
  222.     this._target = null;
  223.     this._selection = null;
  224.   },
  225.  
  226.   /////////////////////////////////////////////////////////////////////////////
  227.   // returns the formatted selection from aNode
  228.   getBTSelection: function (aNode)
  229.   {
  230.     var item;
  231.     switch (aNode.id) {
  232.     case "bookmarks-ptf":
  233.       item = BMSVC.getBookmarksToolbarFolder().Value;
  234.       break;
  235.     case "bookmarks-menu":
  236.       item = this._folderRoot;
  237.       break;
  238.     default:
  239.       item = aNode.id;
  240.       if (!this.isBTBookmark(item))
  241.         return {length:0};
  242.     }
  243.     var parent           = this.getBTContainer(aNode);
  244.     var isExpanded       = aNode.hasAttribute("open") && aNode.open;
  245.     var selection        = {};
  246.     selection.item       = [RDF.GetResource(item)];
  247.     selection.parent     = [RDF.GetResource(parent)];
  248.     selection.isExpanded = [isExpanded];
  249.     selection.length     = selection.item.length;
  250.     BookmarksUtils.checkSelection(selection);
  251.     return selection;
  252.   },
  253.  
  254.   /////////////////////////////////////////////////////////////////////////
  255.   // returns the insertion target from aNode
  256.   getBTTarget: function (aNode, aOrientation)
  257.   {
  258.     var item, parent, index;
  259.  
  260.     switch (aNode.id) {
  261.     case "bookmarks-ptf":
  262.       parent = BMSVC.getBookmarksToolbarFolder().Value;
  263.       item = BookmarksToolbar.getLastVisibleBookmark();
  264.       break;
  265.     case "bookmarks-menu":
  266.       parent = this._folderRoot;
  267.       break;
  268.     case "bookmarks-chevron":
  269.       parent = BMSVC.getBookmarksToolbarFolder().Value;
  270.       break;
  271.     default:
  272.       if (aOrientation == BookmarksUtils.DROP_ON)
  273.         parent = aNode.id
  274.       else {
  275.         parent = this.getBTContainer(aNode);
  276.         item = aNode;
  277.       }
  278.     }
  279.     parent = RDF.GetResource(parent);
  280.     if (aOrientation == BookmarksUtils.DROP_ON)
  281.       return BookmarksUtils.getTargetFromFolder(parent);
  282.  
  283.     item = RDF.GetResource(item.id);
  284.     RDFC.Init(BMDS, parent);
  285.     index = RDFC.IndexOf(item);
  286.     if (aOrientation == BookmarksUtils.DROP_AFTER)
  287.       ++index;
  288.  
  289.     return { parent: parent, index: index };
  290.   },
  291.  
  292.   /////////////////////////////////////////////////////////////////////////
  293.   // returns the parent resource of a node in the personal toolbar.
  294.   // this is determined by inspecting the source element and walking up the 
  295.   // DOM tree to find the appropriate containing node.
  296.   getBTContainer: function (aNode)
  297.   {
  298.     var parent;
  299.     var item = aNode.id;
  300.     if (!this.isBTBookmark(item))
  301.       return this._folderRoot
  302.     parent = aNode.parentNode.parentNode;
  303.     parent = parent.id;
  304.     switch (parent) {
  305.     case "bookmarks-chevron":
  306.     case "bookmarks-stack":
  307.     case "bookmarks-toolbar":
  308.       return BMSVC.getBookmarksToolbarFolder().Value;
  309.     case "bookmarks-menu":
  310.       return this._folderRoot;
  311.     default:
  312.       return parent;
  313.     }
  314.   },
  315.  
  316.   ///////////////////////////////////////////////////////////////////////////
  317.   // returns true if the node is a bookmark, a folder or a bookmark separator
  318.   isBTBookmark: function (aURI)
  319.   {
  320.     if (!aURI || aURI == "bookmarkAllCmd")
  321.       return false;
  322.     var type = BookmarksUtils.resolveType(aURI);
  323.     return (type == "BookmarkSeparator"     ||
  324.             type == "Bookmark"              ||
  325.             type == "Folder"                ||
  326.             type == "PersonalToolbarFolder" ||
  327.             type == "Livemark"              ||
  328.             type == "ImmutableBookmark"     ||
  329.             type == "ImmutableFolder"       ||
  330.             aURI == "bookmarks-ptf")
  331.   },
  332.  
  333.   /////////////////////////////////////////////////////////////////////////
  334.   // returns true if the node is a container. -->
  335.   isBTContainer: function (aTarget)
  336.   {
  337.     return  aTarget.localName == "menu" || (aTarget.localName == "toolbarbutton" &&
  338.            (aTarget.getAttribute("container") == "true"));
  339.   },
  340.  
  341.   /////////////////////////////////////////////////////////////////////////
  342.   // returns BookmarksUtils.DROP_BEFORE, DROP_ON or DROP_AFTER accordingly
  343.   // to the event coordinates. Skin authors could break us, we'll cross that 
  344.   // bridge when they turn us 90degrees.  -->
  345.   getBTOrientation: function (aEvent, aTarget)
  346.   {
  347.     var target
  348.     if (!aTarget)
  349.       target = aEvent.target;
  350.     else
  351.       target = aTarget;
  352.     if (target.localName == "menu"                 &&
  353.         target.parentNode.localName != "menupopup" ||
  354.         target.id == "bookmarks-chevron")
  355.       return BookmarksUtils.DROP_ON;
  356.     if (target.id == "bookmarks-ptf") {
  357.       return target.hasChildNodes()?
  358.              BookmarksUtils.DROP_AFTER:BookmarksUtils.DROP_ON;
  359.     }
  360.  
  361.     var overButtonBoxObject = target.boxObject.QueryInterface(Components.interfaces.nsIBoxObject);
  362.     var overParentBoxObject = target.parentNode.boxObject.QueryInterface(Components.interfaces.nsIBoxObject);
  363.  
  364.     var size, border;
  365.     var coordValue, clientCoordValue;
  366.     switch (target.localName) {
  367.       case "toolbarseparator":
  368.       case "toolbarbutton":
  369.         size = overButtonBoxObject.width;
  370.         coordValue = overButtonBoxObject.x;
  371.         clientCoordValue = aEvent.clientX;
  372.         break;
  373.       case "menuseparator": 
  374.       case "menu":
  375.       case "menuitem":
  376.         size = overButtonBoxObject.height;
  377.         coordValue = overButtonBoxObject.screenY;
  378.         clientCoordValue = aEvent.screenY;
  379.         break;
  380.       default: return BookmarksUtils.DROP_ON;
  381.     }
  382.     if (this.isBTContainer(target))
  383.       if (target.localName == "toolbarbutton") {
  384.         // the DROP_BEFORE area excludes the label
  385.         var iconNode = document.getAnonymousElementByAttribute(target, "class", "toolbarbutton-icon");
  386.         border = parseInt(document.defaultView.getComputedStyle(target,"").getPropertyValue("padding-left")) +
  387.                  parseInt(document.defaultView.getComputedStyle(iconNode     ,"").getPropertyValue("width"));
  388.         border = Math.min(size/5,Math.max(border,4));
  389.       } else
  390.         border = size/5;
  391.     else
  392.       border = size/2;
  393.  
  394.     // in the first region?
  395.     if (clientCoordValue-coordValue < border)
  396.       return BookmarksUtils.DROP_BEFORE;
  397.     // in the last region?
  398.     else if (clientCoordValue-coordValue >= size-border)
  399.       return BookmarksUtils.DROP_AFTER;
  400.     else // must be in the middle somewhere
  401.       return BookmarksUtils.DROP_ON;
  402.   },
  403.  
  404.   /////////////////////////////////////////////////////////////////////////
  405.   // expand the folder targeted by the context menu.
  406.   expandBTFolder: function ()
  407.   {
  408.     var target = document.popupNode.lastChild;
  409.     if (document.popupNode.open)
  410.       target.hidePopup();
  411.     else
  412.       target.showPopup(document.popupNode);
  413.   },
  414.  
  415.   onCommandUpdate: function ()
  416.   {
  417.     var selection = this._selection;
  418.     var target    = this._target;
  419.     BookmarksController.onCommandUpdate(selection, target);
  420.     if (document.popupNode.id == "bookmarks-ptf") {
  421.       // disabling 'cut' and 'copy' on the empty area of the personal toolbar
  422.       var commandNode = document.getElementById("cmd_cut");
  423.       commandNode.setAttribute("disabled", "true");
  424.       commandNode = document.getElementById("cmd_copy");
  425.       commandNode.setAttribute("disabled", "true");
  426.     }
  427.   },
  428.  
  429.   ///////////////////////////////////////////////////////////////
  430.   // Load a bookmark in menus or toolbar buttons
  431.   // aTarget may not the aEvent target (see Open all in tabs command)
  432.   loadBookmark: function (aEvent, aTarget, aDS)
  433.   {
  434.     if (aTarget.getAttribute("class") == "openintabs-menuitem")
  435.       aTarget = aTarget.parentNode.parentNode;
  436.       
  437.     // Check for invalid bookmarks (most likely a static menu item like "Manage Bookmarks")
  438.     if (!this.isBTBookmark(aTarget.id))
  439.       return;
  440.     var rSource   = RDF.GetResource(aTarget.id);
  441.     var selection = BookmarksUtils.getSelectionFromResource(rSource);
  442.     var browserTarget = whereToOpenLink(aEvent);
  443.     BookmarksCommand.openBookmark(selection, browserTarget, aDS);
  444.     aEvent.stopPropagation();
  445.   },
  446.  
  447.   ////////////////////////////////////////////////
  448.   // loads a bookmark with the mouse middle button
  449.   loadBookmarkMiddleClick: function (aEvent, aDS)
  450.   {
  451.     if (aEvent.button != 1)
  452.       return;
  453.     // unlike for command events, we have to close the menus manually
  454.     BookmarksMenuDNDObserver.mCurrentDragOverTarget = null;
  455.     BookmarksMenuDNDObserver.onDragCloseTarget();
  456.     this.loadBookmark(aEvent, aEvent.target, aDS);
  457.   }
  458. }
  459.  
  460. var BookmarksMenuController = {
  461.  
  462.   supportsCommand: BookmarksController.supportsCommand,
  463.  
  464.   isCommandEnabled: function (aCommand)
  465.   {
  466.     var selection = BookmarksMenu._selection;
  467.     var target    = BookmarksMenu._target;
  468.     if (selection)
  469.       return BookmarksController.isCommandEnabled(aCommand, selection, target);
  470.     return false;
  471.   },
  472.  
  473.   doCommand: function (aCommand)
  474.   {
  475.     // we needed to focus the element that has the bm command controller
  476.     // to get here. Now, let's focus the content before performing the command:
  477.     // if a modal dialog is called from now, the content will be focused again
  478.     // automatically after dismissing the dialog
  479.     if (content)
  480.       content.focus();
  481.     BookmarksMenuDNDObserver.onDragRemoveFeedBack(document.popupNode);
  482.  
  483.     // if a dialog opens, the "open" attribute of a menuitem-container
  484.     // clicked on won't be removed. We do it manually.
  485.     var element = document.popupNode.firstChild;
  486.     if (element && element.localName == "menupopup")
  487.       element.hidePopup();
  488.  
  489.     var selection = BookmarksMenu._selection;
  490.     var target    = BookmarksMenu._target;
  491.     var db        = BookmarksMenu._db;
  492.     switch (aCommand) {
  493.     case "cmd_bm_expandfolder":
  494.       BookmarksMenu.expandBTFolder();
  495.       break;
  496.     default:
  497.       BookmarksController.doCommand(aCommand, selection, target, db);
  498.     }
  499.   },
  500.  
  501.   onMouseMove: function (aEvent)
  502.   {
  503.     var command = aEvent.target.getAttribute("command");
  504.     var isDisabled = aEvent.target.getAttribute("disabled")
  505.     if (isDisabled != "true" && (command == "cmd_bm_newfolder" || command == "cmd_paste")) {
  506.       BookmarksMenuDNDObserver.onDragSetFeedBack(document.popupNode, BookmarksMenu._orientation);
  507.     } else {
  508.       BookmarksMenuDNDObserver.onDragRemoveFeedBack(document.popupNode);
  509.     }
  510.   }
  511. }
  512.  
  513. var BookmarksMenuDNDObserver = {
  514.  
  515.   ////////////////////
  516.   // Public methods //
  517.   ////////////////////
  518.  
  519.   onDragStart: function (aEvent, aXferData, aDragAction)
  520.   {
  521.     var target = aEvent.target;
  522.  
  523.     // Prevent dragging from invalid regions
  524.  
  525.     // can't drag from the empty areas
  526.     if (target.id == "bookmarks-menu" ||
  527.         target.id == "bookmarks-chevron" ||
  528.         target.id == "bookmarks-ptf")
  529.       return false;
  530.  
  531.     if (!BookmarksMenu.isBTBookmark(target.id))
  532.       return false;
  533.  
  534.     // Prevent dragging out of menupopups on non Win32 platforms. 
  535.     // a) on Mac drag from menus is generally regarded as being satanic
  536.     // b) on Linux, this causes an X-server crash, (bug 151336)
  537.     // c) on Windows, there is no hang or crash associated with this, so we'll leave 
  538.     // the functionality there. 
  539.     if (navigator.platform != "Win32" && target.localName != "toolbarbutton")
  540.       return false;
  541.  
  542.     // a drag start is fired when leaving an open toolbarbutton(type=menu) 
  543.     // (see bug 143031)
  544.     if (this.isContainer(target)) {
  545.       if (this.isPlatformNotSupported) 
  546.         return false;
  547.       if (!aEvent.shiftKey && !aEvent.altKey && !aEvent.ctrlKey)
  548.         return false;
  549.       // menus open on mouse down
  550.       target.firstChild.hidePopup();
  551.     }
  552.     var selection  = BookmarksMenu.getBTSelection(target);
  553.     aXferData.data = BookmarksUtils.getXferDataFromSelection(selection);
  554.     return true;
  555.   },
  556.  
  557.   onDragOver: function(aEvent, aFlavour, aDragSession) 
  558.   {
  559.     var orientation = BookmarksMenu.getBTOrientation(aEvent)
  560.     if (aDragSession.canDrop)
  561.       this.onDragSetFeedBack(aEvent.target, orientation);
  562.     if (orientation != this.mCurrentDropPosition) {
  563.       // emulating onDragExit and onDragEnter events since the drop region
  564.       // has changed on the target.
  565.       this.onDragExit(aEvent, aDragSession);
  566.       this.onDragEnter(aEvent, aDragSession);
  567.     }
  568.     if (this.isPlatformNotSupported)
  569.       return;
  570.     if (this.isTimerSupported || !aDragSession.sourceNode)
  571.       return;
  572.     this.onDragOverCheckTimers();
  573.   },
  574.  
  575.   onDragEnter: function (aEvent, aDragSession)
  576.   {
  577.     var target = aEvent.target;
  578.     var orientation = BookmarksMenu.getBTOrientation(aEvent);
  579.     if (target.localName == "menupopup" || target.id == "bookmarks-ptf")
  580.       target = target.parentNode;
  581.     if (aDragSession.canDrop) {
  582.       this.onDragSetFeedBack(target, orientation);
  583.       this.onDragEnterSetTimer(target, aDragSession);
  584.     }
  585.     this.mCurrentDragOverTarget = target;
  586.     this.mCurrentDropPosition   = orientation;
  587.   },
  588.  
  589.   onDragExit: function (aEvent, aDragSession)
  590.   {
  591.     var target = aEvent.target;
  592.     if (target.localName == "menupopup" || target.id == "bookmarks-ptf")
  593.       target = target.parentNode;
  594.     this.onDragRemoveFeedBack(target);
  595.     this.onDragExitSetTimer(target, aDragSession);
  596.     this.mCurrentDragOverTarget = null;
  597.     this.mCurrentDropPosition = null;
  598.   },
  599.  
  600.   onDrop: function (aEvent, aXferData, aDragSession)
  601.   {
  602.     var target = aEvent.target;
  603.     this.onDragRemoveFeedBack(target);
  604.  
  605.     var selection = BookmarksUtils.getSelectionFromXferData(aDragSession);
  606.  
  607.     var orientation = BookmarksMenu.getBTOrientation(aEvent);
  608.  
  609.     // For RTL PersonalBar bookmarks buttons, orientation should be inverted (only in drop case)
  610.     var PBStyle = window.getComputedStyle(document.getElementById("PersonalToolbar"),'');
  611.     var isHorizontal = (target.localName == "toolbarbutton");
  612.     if ((PBStyle.direction == 'rtl') && isHorizontal) {
  613.       if (orientation == BookmarksUtils.DROP_AFTER)
  614.         orientation = BookmarksUtils.DROP_BEFORE;
  615.       else if (orientation == BookmarksUtils.DROP_BEFORE)
  616.         orientation = BookmarksUtils.DROP_AFTER;
  617.     }
  618.  
  619.     var selTarget   = BookmarksMenu.getBTTarget(target, orientation);
  620.  
  621.     // we can only test for kCopyAction if the source is a bookmark
  622.     var checkCopy = aDragSession.isDataFlavorSupported("moz/rdfitem");
  623.  
  624.     const kDSIID      = Components.interfaces.nsIDragService;
  625.     const kCopyAction = kDSIID.DRAGDROP_ACTION_COPY + kDSIID.DRAGDROP_ACTION_LINK;
  626.  
  627.     // hide the 'open in tab' menuseparator because bookmarks
  628.     // can be inserted after it if they are dropped after the last bookmark
  629.     // a more comprehensive fix would be in the menupopup template builder
  630.     var menuSeparator = null;
  631.     var menuTarget = (target.localName == "toolbarbutton" ||
  632.                       target.localName == "menu")         && 
  633.                      orientation == BookmarksUtils.DROP_ON?
  634.                      target.lastChild:target.parentNode;
  635.     if (menuTarget.hasChildNodes() &&
  636.         menuTarget.lastChild.getAttribute("class") == "openintabs-menuitem") {
  637.       menuSeparator = menuTarget.lastChild.previousSibling;
  638.       menuTarget.removeChild(menuSeparator);
  639.     }
  640.  
  641.     // doCopy defaults to true; check if we should make it false.
  642.     // we make it false only if all the selection items have valid parent
  643.     // bookmark DS containers (i.e. aren't generated via aggregation)
  644.     var doCopy = true;
  645.     if (checkCopy && !(aDragSession.dragAction & kCopyAction))
  646.       doCopy = BookmarksUtils.shouldCopySelection("drag", selection);
  647.  
  648.     if (doCopy)
  649.       BookmarksUtils.insertAndCheckSelection("drag", selection, selTarget);
  650.     else
  651.       BookmarksUtils.moveAndCheckSelection("drag", selection, selTarget);
  652.  
  653.     // show again the menuseparator
  654.     if (menuSeparator)
  655.       menuTarget.insertBefore(menuSeparator, menuTarget.lastChild);
  656.  
  657.   },
  658.  
  659.   canDrop: function (aEvent, aDragSession)
  660.   {
  661.     var target = aEvent.target;
  662.     if (!BookmarksMenu.isBTBookmark(target.id))
  663.       return false;
  664.  
  665.     // Bug 7389, 7996: only allow to drop URLs here
  666.     if (!aDragSession.isDataFlavorSupported("text/x-moz-url")) {
  667.       var transferable = Components.classes["@mozilla.org/widget/transferable;1"].createInstance(Components.interfaces.nsITransferable);
  668.       transferable.addDataFlavor("text/unicode");
  669.       aDragSession.getData(transferable, 0);
  670.       var flavor = {};
  671.       var data = {};
  672.       var length = {};
  673.       transferable.getAnyTransferData(flavor, data, length);
  674.       var text = data.value.QueryInterface(Components.interfaces.nsISupportsString).data;
  675.       var result = text.match(/(file|ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?/);
  676.  
  677.       // No match, or the match is not the complete string.
  678.       // (There is an URL, but the text itself is not an URL.)
  679.       if (!result || (result[0] != text)) {
  680.         return false;
  681.       }
  682.     }
  683.  
  684.     var btype = BookmarksUtils.resolveType(target.id);
  685.  
  686.     return target.id == "bookmarks-menu"               ||
  687.            target.id == "bookmarks-chevron"            ||
  688.            target.id == "bookmarks-ptf" ||
  689.           (target.id != "NC:SystemBookmarksStaticRoot" &&
  690.            btype == "Folder" ||
  691.            btype == "Bookmark");
  692.   },
  693.  
  694.   canHandleMultipleItems: true,
  695.  
  696.   getSupportedFlavours: function () 
  697.   {
  698.     var flavourSet = new FlavourSet();
  699.     flavourSet.appendFlavour("moz/rdfitem");
  700.     flavourSet.appendFlavour("text/x-moz-url");
  701.     flavourSet.appendFlavour("application/x-moz-file", "nsIFile");
  702.     flavourSet.appendFlavour("text/unicode");
  703.     return flavourSet;
  704.   }, 
  705.   
  706.  
  707.   ////////////////////////////////////
  708.   // Private methods and properties //
  709.   ////////////////////////////////////
  710.  
  711.   springLoadedMenuDelay: 350, // milliseconds
  712.   isPlatformNotSupported: navigator.platform.indexOf("Mac") != -1, // see bug 136524
  713.   // Needs to be dynamically overridden (to |true|) in the case of an external drag: see bug 232795.
  714.   isTimerSupported: navigator.platform.indexOf("Win") == -1,
  715.  
  716.   mCurrentDragOverTarget: null,
  717.   mCurrentDropPosition: null,
  718.   loadTimer  : null,
  719.   closeTimer : null,
  720.   loadTarget : null,
  721.   closeTarget: null,
  722.  
  723.   _observers : null,
  724.   get mObservers ()
  725.   {
  726.     if (!this._observers) {
  727.       this._observers = [
  728.         document.getElementById("bookmarks-ptf"),
  729.         // menubar menus haven't an "open" attribute: we can take the child
  730.         document.getElementById("bookmarks-menu").firstChild,
  731.         document.getElementById("bookmarks-chevron").parentNode
  732.       ]
  733.     }
  734.     return this._observers;
  735.   },
  736.  
  737.   getObserverForNode: function (aNode)
  738.   {
  739.     if (!aNode)
  740.       return null;
  741.     var node = aNode;
  742.     var observer;
  743.     while (node) {
  744.       for (var i=0; i < this.mObservers.length; i++) {
  745.         observer = this.mObservers[i];
  746.         if (observer == node)
  747.           return observer;
  748.       }
  749.       node = node.parentNode;
  750.     }
  751.     return null;
  752.   },
  753.  
  754.   onDragCloseMenu: function (aNode)
  755.   {
  756.     var children = aNode.childNodes;
  757.     for (var i = 0; i < children.length; i++) {
  758.       if (this.isContainer(children[i]) && 
  759.           children[i].getAttribute("open") == "true") {
  760.         this.onDragCloseMenu(children[i].lastChild);
  761.         if (children[i] != this.mCurrentDragOverTarget || this.mCurrentDropPosition != BookmarksUtils.DROP_ON)
  762.           children[i].lastChild.hidePopup();
  763.       }
  764.     } 
  765.   },
  766.  
  767.   onDragCloseTarget: function ()
  768.   {
  769.     var currentObserver = this.getObserverForNode(this.mCurrentDragOverTarget);
  770.     // close all the menus not hovered by the mouse
  771.     for (var i=0; i < this.mObservers.length; i++) {
  772.       if (currentObserver != this.mObservers[i]) {
  773.         this.onDragCloseMenu(this.mObservers[i]);
  774.         if (this.mObservers[i].parentNode.id == "bookmarks-menu")
  775.           this.mObservers[i].hidePopup();
  776.       } else
  777.         this.onDragCloseMenu(this.mCurrentDragOverTarget.parentNode);
  778.     }
  779.   },
  780.  
  781.   onDragLoadTarget: function (aTarget) 
  782.   {
  783.     if (!this.mCurrentDragOverTarget)
  784.       return;
  785.     // Load the current menu
  786.     if (this.mCurrentDropPosition == BookmarksUtils.DROP_ON && 
  787.         this.isContainer(aTarget))
  788.       aTarget.lastChild.showPopup(aTarget);
  789.   },
  790.  
  791.   onDragOverCheckTimers: function ()
  792.   {
  793.     var now = new Date().getTime();
  794.     if (this.closeTimer && now-this.springLoadedMenuDelay>this.closeTimer) {
  795.       this.onDragCloseTarget();
  796.       this.closeTimer = null;
  797.     }
  798.     if (this.loadTimer && (now-this.springLoadedMenuDelay>this.loadTimer)) {
  799.       this.onDragLoadTarget(this.loadTarget);
  800.       this.loadTimer = null;
  801.     }
  802.   },
  803.  
  804.   onDragEnterSetTimer: function (aTarget, aDragSession)
  805.   {
  806.     if (this.isPlatformNotSupported)
  807.       return;
  808.     if (this.isTimerSupported || !aDragSession.sourceNode) {
  809.       var targetToBeLoaded = aTarget;
  810.       clearTimeout(this.loadTimer);
  811.       if (aTarget == aDragSession.sourceNode)
  812.         return;
  813.       var This = this;
  814.       this.loadTimer=setTimeout(function () {This.onDragLoadTarget(targetToBeLoaded)}, This.springLoadedMenuDelay);
  815.     } else {
  816.       var now = new Date().getTime();
  817.       this.loadTimer  = now;
  818.       this.loadTarget = aTarget;
  819.     }
  820.   },
  821.  
  822.   onDragExitSetTimer: function (aTarget, aDragSession)
  823.   {
  824.     if (this.isPlatformNotSupported)
  825.       return;
  826.     var This = this;
  827.     if (this.isTimerSupported || !aDragSession.sourceNode) {
  828.       clearTimeout(this.closeTimer)
  829.       this.closeTimer=setTimeout(function () {This.onDragCloseTarget()}, This.springLoadedMenuDelay);
  830.     } else {
  831.       var now = new Date().getTime();
  832.       this.closeTimer  = now;
  833.       this.closeTarget = aTarget;
  834.       this.loadTimer = null;
  835.  
  836.       // If the user isn't rearranging within the menu, close it
  837.       // To do so, we exploit a Mac bug: timeout set during
  838.       // drag and drop on Windows and Mac are fired only after that the drop is released.
  839.       // timeouts will pile up, we may have a better approach but for the moment, this one
  840.       // correctly close the menus after a drop/cancel outside the personal toolbar.
  841.       // The if statement in the function has been introduced to deal with rare but reproducible
  842.       // missing Exit events.
  843.       if (aDragSession.sourceNode.localName != "menuitem" && aDragSession.sourceNode.localName != "menu")
  844.         setTimeout(function () { if (This.mCurrentDragOverTarget) {This.onDragRemoveFeedBack(This.mCurrentDragOverTarget); This.mCurrentDragOverTarget=null} This.loadTimer=null; This.onDragCloseTarget() }, 0);
  845.     }
  846.   },
  847.  
  848.   onDragSetFeedBack: function (aTarget, aOrientation)
  849.   {
  850.    switch (aTarget.localName) {
  851.       case "toolbarseparator":
  852.       case "toolbarbutton":
  853.         switch (aOrientation) {
  854.           case BookmarksUtils.DROP_BEFORE: 
  855.             aTarget.setAttribute("dragover-left", "true");
  856.             break;
  857.           case BookmarksUtils.DROP_AFTER:
  858.             aTarget.setAttribute("dragover-right", "true");
  859.             break;
  860.           case BookmarksUtils.DROP_ON:
  861.             aTarget.setAttribute("dragover-top"   , "true");
  862.             aTarget.setAttribute("dragover-bottom", "true");
  863.             aTarget.setAttribute("dragover-left"  , "true");
  864.             aTarget.setAttribute("dragover-right" , "true");
  865.             break;
  866.         }
  867.         break;
  868.       case "menuseparator": 
  869.       case "menu":
  870.       case "menuitem":
  871.         switch (aOrientation) {
  872.           case BookmarksUtils.DROP_BEFORE: 
  873.             aTarget.setAttribute("dragover-top", "true");
  874.             break;
  875.           case BookmarksUtils.DROP_AFTER:
  876.             aTarget.setAttribute("dragover-bottom", "true");
  877.             break;
  878.           case BookmarksUtils.DROP_ON:
  879.             break;
  880.         }
  881.         break;
  882.       case "hbox"     : 
  883.         // hit between the last visible bookmark and the chevron
  884.         var newTarget = BookmarksToolbar.getLastVisibleBookmark();
  885.         if (newTarget)
  886.           newTarget.setAttribute("dragover-right", "true");
  887.         break;
  888.       case "stack"    :
  889.       case "menupopup": break; 
  890.      default: dump("No feedback for: "+aTarget.localName+"\n");
  891.     }
  892.   },
  893.  
  894.   onDragRemoveFeedBack: function (aTarget)
  895.   { 
  896.     var newTarget;
  897.     var bt;
  898.     if (aTarget.id == "bookmarks-ptf") { 
  899.       // hit when dropping in the bt or between the last visible bookmark 
  900.       // and the chevron
  901.       newTarget = BookmarksToolbar.getLastVisibleBookmark();
  902.       if (newTarget)
  903.         newTarget.removeAttribute("dragover-right");
  904.     } else if (aTarget.id == "bookmarks-stack") {
  905.       newTarget = BookmarksToolbar.getLastVisibleBookmark();
  906.       newTarget.removeAttribute("dragover-right");
  907.     } else {
  908.       aTarget.removeAttribute("dragover-left");
  909.       aTarget.removeAttribute("dragover-right");
  910.       aTarget.removeAttribute("dragover-top");
  911.       aTarget.removeAttribute("dragover-bottom");
  912.     }
  913.   },
  914.  
  915.   onDropSetFeedBack: function (aTarget)
  916.   {
  917.     //XXX Not yet...
  918.   },
  919.  
  920.   isContainer: function (aTarget)
  921.   {
  922.     return aTarget.localName == "menu"          || 
  923.            aTarget.localName == "toolbarbutton" &&
  924.            aTarget.getAttribute("type") == "menu";
  925.   }
  926. }
  927.  
  928. var BookmarksToolbar = 
  929. {
  930.   /////////////////////////////////////////////////////////////////////////////
  931.   // make bookmarks toolbar act like menus
  932.   openedMenuButton:null,
  933.   autoOpenMenu: function (aEvent)
  934.   {
  935.     var target = aEvent.target;
  936.     if (BookmarksToolbar.openedMenuButton != target &&
  937.         target.nodeName == "toolbarbutton" &&
  938.         target.type == "menu") {
  939.       BookmarksToolbar.openedMenuButton.open = false;
  940.       target.open = true;
  941.     }
  942.   },
  943.   setOpenedMenu: function (aEvent)
  944.   {
  945.     if (aEvent.target.parentNode.localName == 'toolbarbutton') {
  946.       if (!this.openedMenuButton)
  947.         aEvent.currentTarget.addEventListener("mouseover", this.autoOpenMenu, true);
  948.       this.openedMenuButton = aEvent.target.parentNode;
  949.     }
  950.   },
  951.   unsetOpenedMenu: function (aEvent)
  952.   {
  953.     if (aEvent.target.parentNode.localName == 'toolbarbutton') {
  954.       aEvent.currentTarget.removeEventListener("mouseover", this.autoOpenMenu, true);
  955.       this.openedMenuButton = null;
  956.     }
  957.   },
  958.  
  959.   /////////////////////////////////////////////////////////////////////////////
  960.   // returns the node of the last visible bookmark on the toolbar -->
  961.   getLastVisibleBookmark: function ()
  962.   {
  963.     var buttons = document.getElementById("bookmarks-ptf");
  964.     var button = buttons.firstChild;
  965.     if (!button)
  966.       return null; // empty bookmarks toolbar
  967.     do {
  968.       if (button.collapsed)
  969.         return button.previousSibling;
  970.       button = button.nextSibling;
  971.     } while (button)
  972.     return buttons.lastChild;
  973.   },
  974.  
  975.   updateOverflowMenu: function (aMenuPopup)
  976.   {
  977.     var hbox = document.getElementById("bookmarks-ptf");
  978.     for (var i = 0; i < hbox.childNodes.length; i++) {
  979.       var button = hbox.childNodes[i];
  980.       var menu = aMenuPopup.childNodes[i];
  981.       if (menu.collapsed == button.collapsed)
  982.         menu.collapsed = !menu.collapsed;
  983.     }
  984.   },
  985.  
  986.   resizeFunc: function(event) 
  987.   { 
  988.     BookmarksToolbarRDFObserver._overflowTimerInEffect = false;
  989.     if (event && event.type == 'focus') 
  990.       window.removeEventListener('focus', BookmarksToolbar.resizeFunc, false); // hack for bug 266737
  991.     var buttons = document.getElementById("bookmarks-ptf");
  992.     if (!buttons)
  993.       return;
  994.     var chevron = document.getElementById("bookmarks-chevron");
  995.     var width = window.innerWidth;
  996.     if (width == 0) {  // hack for bug 266737
  997.       window.addEventListener('focus', BookmarksToolbar.resizeFunc, false);
  998.       return;
  999.     }
  1000.  
  1001.     if (buttons.childNodes.length == 0) {
  1002.       chevron.collapsed = true;
  1003.       return;
  1004.     }
  1005.  
  1006.     var myToolbar = buttons.parentNode.parentNode.parentNode;
  1007.     for (var i = myToolbar.childNodes.length-1; i >= 0; i--){
  1008.       var anItem = myToolbar.childNodes[i];
  1009.       if (anItem.id == "personal-bookmarks") {
  1010.         break;
  1011.       }
  1012.       width -= anItem.boxObject.width;
  1013.     }
  1014.     var chevronWidth = 0;
  1015.     chevron.collapsed = false;
  1016.     chevronWidth = chevron.boxObject.width;
  1017.     chevron.collapsed = true;
  1018.     var overflowed = false;
  1019.  
  1020.     var isLTR=window.getComputedStyle(document.getElementById("PersonalToolbar"),'').direction=='ltr';
  1021.  
  1022.     for (var i=0; i<buttons.childNodes.length; i++) {
  1023.       var button = buttons.childNodes[i];
  1024.       button.collapsed = overflowed;
  1025.       
  1026.       if (i == buttons.childNodes.length - 1) // last ptf item...
  1027.         chevronWidth = 0;
  1028.       var offset = isLTR ? button.boxObject.x 
  1029.                          : width - button.boxObject.x;
  1030.       if (offset + button.boxObject.width + chevronWidth > width) {
  1031.          overflowed = true;
  1032.         // This button doesn't fit. Show it in the menu. Hide it in the toolbar.
  1033.         if (!button.collapsed)
  1034.           button.collapsed = true;
  1035.         if (chevron.collapsed) {
  1036.           chevron.collapsed = false;
  1037.           var overflowPadder = document.getElementById("overflow-padder");
  1038.           offset = isLTR ? buttons.boxObject.x 
  1039.                          : width - buttons.boxObject.x - buttons.boxObject.width;
  1040.           overflowPadder.width = width - chevron.boxObject.width - offset;
  1041.         }
  1042.       }
  1043.     }
  1044.   },
  1045.  
  1046.   // Fill in tooltips for personal toolbar
  1047.   fillInBTTooltip: function (tipElement)
  1048.   {
  1049.  
  1050.     var title = tipElement.label;
  1051.     var url = tipElement.statusText;
  1052.  
  1053.     if (!title && !url) {
  1054.       // bail out early if there is nothing to show
  1055.       return false;
  1056.     }
  1057.  
  1058.     var tooltipTitle = document.getElementById("btTitleText");
  1059.     var tooltipUrl = document.getElementById("btUrlText"); 
  1060.     if (title && title != url) {
  1061.       tooltipTitle.removeAttribute("hidden");
  1062.       tooltipTitle.setAttribute("value", title);
  1063.     } else  {
  1064.       tooltipTitle.setAttribute("hidden", "true");
  1065.     }
  1066.     if (url) {
  1067.       tooltipUrl.removeAttribute("hidden");
  1068.       tooltipUrl.setAttribute("value", url);
  1069.     } else {
  1070.       tooltipUrl.setAttribute("hidden", "true");
  1071.     }
  1072.     return true; // show tooltip
  1073.   }
  1074. }
  1075.  
  1076. // Implement nsIRDFObserver so we can update our overflow state when items get
  1077. // added/removed from the toolbar
  1078. var BookmarksToolbarRDFObserver =
  1079. {
  1080.   onAssert: function (aDataSource, aSource, aProperty, aTarget)
  1081.   {
  1082.     if (aProperty.Value == gNC_NS+"BookmarksToolbarFolder") {
  1083.       var bt = document.getElementById("bookmarks-ptf");
  1084.       if (bt) {
  1085.         bt.ref = aSource.Value;
  1086.         document.getElementById("bookmarks-chevron").ref = aSource.Value;
  1087.       }
  1088.     }
  1089.     this.setOverflowTimeout(aSource, aProperty);
  1090.   },
  1091.   onUnassert: function (aDataSource, aSource, aProperty, aTarget)
  1092.   {
  1093.     this.setOverflowTimeout(aSource, aProperty);
  1094.   },
  1095.   onChange: function (aDataSource, aSource, aProperty, aOldTarget, aNewTarget)
  1096.   {
  1097.     this.setOverflowTimeout(aSource, aProperty);
  1098.   },
  1099.   onMove: function (aDataSource, aOldSource, aNewSource, aProperty, aTarget) {},
  1100.   onBeginUpdateBatch: function (aDataSource) {},
  1101.   onEndUpdateBatch: function (aDataSource)
  1102.   {
  1103.     this._overflowTimerInEffect = true;
  1104.     setTimeout(BookmarksToolbar.resizeFunc, 0, null);
  1105.   },
  1106.   _overflowTimerInEffect: false,
  1107.   setOverflowTimeout: function (aSource, aProperty)
  1108.   {
  1109.     if (this._overflowTimerInEffect)
  1110.       return;
  1111.     if (aProperty.Value == gWEB_NS+"LastModifiedDate")
  1112.       return;
  1113.     this._overflowTimerInEffect = true;
  1114.     setTimeout(BookmarksToolbar.resizeFunc, 0, null);
  1115.   }
  1116. }
  1117.